/*******************************************************************************
 * WayStudio GUI Library
 * Developer:Xu Waycell
*******************************************************************************/
#include <widget.hpp>
#include <widgetimpl.hpp>
#include <layout.hpp>

#include <guievent.hpp>
#include <renderevent.hpp>
#include <resizeevent.hpp>
#include <moveevent.hpp>
#include <inputevent.hpp>
#include <cursorevent.hpp>
#include <keyevent.hpp>

BEGIN_SOURCECODE

BEGIN_WS_NAMESPACE

Map<Widget*, WidgetSpecific*> WidgetSpecific::SpecificMap;

WidgetSpecific* WidgetSpecific::Fetch(Widget* P_WIDGET)
{
	if(P_WIDGET)
	{
		Map<Widget*, WidgetSpecific*>::Iterator ITER(WidgetSpecific::SpecificMap.Find(P_WIDGET));
		if(ITER!=WidgetSpecific::SpecificMap.End())
			return (*ITER).Second;
	}
	return 0;
}

#if defined(WS_MSWINDOWS)

Map<HWND, WidgetSpecific*> WidgetSpecific::HandleMap;

LRESULT CALLBACK Widget::WidgetImplementation::WindowProc(HWND H_WND, UINT I_MSG, WPARAM W_PARAM, LPARAM L_PARAM)
{
	switch(I_MSG)
	{
	case WM_NCCREATE:
	case WM_NCCALCSIZE:
	case WM_NCDESTROY:
	case WM_NCACTIVATE:
	case WM_GETMINMAXINFO:
	case WM_WINDOWPOSCHANGING:
		return DefWindowProc(H_WND, I_MSG, W_PARAM, L_PARAM);
		break;
	case WM_CREATE:
		CREATESTRUCT* P_STRUCT = reinterpret_cast<CREATESTRUCT*>(L_PARAM);
		if(P_STRUCT)
			if(P_STRUCT->lpCreateParams)
			{
				WidgetSpecific* H_Specific = reinterpret_cast<WidgetSpecific*>(P_STRUCT->lpCreateParams);
				H_Specific->H_Handle = H_WND;
				WidgetSpecific::HandleMap.Append(H_Specific->H_Handle, H_Specific);
				if(P_STRUCT->hwndParent)
					H_Specific->B_WINDOW=false;
				else
					H_Specific->B_WINDOW=true;
				H_Specific->I_Width = P_STRUCT->cx;
				H_Specific->I_Height = P_STRUCT->cy;
				H_Specific->I_X = P_STRUCT->x;
				H_Specific->I_Y = P_STRUCT->y;
				return 0;
			}
		return -1;
		break;
	}
	Map<HWND, WidgetSpecific*>::Iterator ITER = WidgetSpecific::HandleMap.Find(H_WND);
	if(ITER == WidgetSpecific::HandleMap.End())
		return DefWindowProc(H_WND, I_MSG, W_PARAM, L_PARAM);
	else
	{
		WidgetSpecific* H_Specific=(*ITER).Second;
		GUIEvent* H_Event = 0;
		Widget* H_Sender = H_Specific->H_Widget;
		Widget* H_Receiver = H_Specific->H_Widget;
		switch(I_MSG)
		{
		case WM_SHOWWINDOW:
			if(W_PARAM)
				H_Specific->B_Visiable=true;
			else
				H_Specific->B_Visiable=false;
		case WM_WINDOWPOSCHANGED:
		{
			WINDOWPOS* P_POS=reinterpret_cast<WINDOWPOS*>(L_PARAM);
			if(!P_POS)
				break;
			switch(P_POS->flags)
			{
			case SWP_NOMOVE:
				H_Specific->I_Width = P_POS->cx;
				H_Specific->I_Height = P_POS->cy;
				break;
			case SWP_NOSIZE:
				H_Specific->I_X = P_POS->x;
				H_Specific->I_Y = P_POS->y;
				break;
			default:
				H_Specific->I_Width = P_POS->cx;
				H_Specific->I_Height = P_POS->cy;
				H_Specific->I_X = P_POS->x;
				H_Specific->I_Y = P_POS->y;
				break;
				}
			}
			break;
		case WM_DESTROY:
			break;
		case WM_CLOSE:
			break;
		case WM_PAINT:
			H_Event = new RenderEvent();
			break;
//		case WM_ENTERSIZEMOVE:
//			return 1;
//		case WM_EXITSIZEMOVE:
//			return 1;
		case WM_SIZE:
			H_Specific->I_Width = LOWORD(L_PARAM);
			H_Specific->I_Height = HIWORD(L_PARAM);
			if(H_Specific->H_Control)
				MoveWindow(H_Specific->H_Control, 0, 0, H_Specific->I_Width, H_Specific->I_Height, true);
			H_Event = new ResizeEvent(H_Specific->I_Width, H_Specific->I_Height);
			break;
		case WM_MOVE:
			H_Specific->I_X = LOWORD(L_PARAM);
			H_Specific->I_Y = HIWORD(L_PARAM);
			H_Event = new MoveEvent(H_Specific->I_X, H_Specific->I_Y);
			break;
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN:
		case WM_KEYDOWN:
		case WM_LBUTTONUP:
		case WM_RBUTTONUP:
		case WM_MBUTTONUP:
		case WM_KEYUP:
			H_Event = new KeyEvent(Unknow, false);
			break;
		case WM_MOUSEMOVE:
			H_Event = new CursorEvent(LOWORD(L_PARAM), HIWORD(L_PARAM));
			break;
		default:
			return DefWindowProc(H_WND, I_MSG, W_PARAM, L_PARAM);
		}
		if(H_Event && H_Sender && H_Receiver)
			H_Specific->H_Widget->PostEvent(H_Event, H_Receiver, H_Sender);
	}
	return DefWindowProc(H_WND, I_MSG, W_PARAM, L_PARAM);
}

#endif

WidgetSpecific::WidgetSpecific(Widget* W):H_Widget(W), H_Layout(0), B_Visiable(false), I_X(0), I_Y(0), I_Height(0), I_Width(0), I_MaximumHeight(0), I_MaximumWidth(0), I_MinimumHeight(0), I_MinimumWidth(0)
#if defined(WS_MSWINDOWS)
										, H_Handle(NULL)
										, H_Control(NULL)
#endif
{
	WidgetSpecific::SpecificMap.Append(H_Widget, this);
}

WidgetSpecific::~WidgetSpecific()
{
	WidgetSpecific::SpecificMap.Remove(H_Widget);
}

Widget::WidgetImplementation::WidgetImplementation():H_Specific(0) {}

Widget::WidgetImplementation::~WidgetImplementation() {}

void Widget::WidgetImplementation::Initial(Widget* P_WIDGET)
{
	if(!H_Specific)
	{
		H_Specific=new WidgetSpecific(P_WIDGET);
	}
#if defined(WS_X11)
#elif defined(WS_MSWINDOWS)
	WNDCLASS CLASS_WIDGET;
	CLASS_WIDGET.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;
	CLASS_WIDGET.lpfnWndProc = &Widget::WidgetImplementation::WindowProc;
//	CLASS_WIDGET.lpfnWndProc = &DefWindowProc;
	CLASS_WIDGET.cbWndExtra = NULL;
	CLASS_WIDGET.cbClsExtra = NULL;
	CLASS_WIDGET.hInstance = NULL;
	CLASS_WIDGET.hIcon = NULL;
	CLASS_WIDGET.hCursor = LoadCursor(NULL, IDC_ARROW);
	CLASS_WIDGET.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
	CLASS_WIDGET.lpszMenuName = "WS_MENU";
	CLASS_WIDGET.lpszClassName  = "WS_WIDGET";
	if(RegisterClass(&CLASS_WIDGET) == 0)
		DWORD ERR=GetLastError();
	Widget* H_Parent=static_cast<Widget*>(P_WIDGET->GetMaster());
	HWND HWND_Parent = 0;
	if(H_Parent)
	{
		HWND_Parent=WidgetSpecific::Fetch(H_Parent)->H_Handle;
		if(!(H_Specific->H_Handle))
			H_Specific->H_Handle=CreateWindow("WS_WIDGET", "WS_WIDGET", WS_VISIBLE | WS_OVERLAPPED | WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_Parent, NULL, NULL, static_cast<LPVOID>(H_Specific));
	}
	else
		if(!(H_Specific->H_Handle))
			H_Specific->H_Handle=CreateWindow("WS_WIDGET", "WS_WIDGET", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_Parent, NULL, NULL, static_cast<LPVOID>(H_Specific));
#endif
}

void Widget::WidgetImplementation::Cleanup()
{
	if(H_Specific)
		delete H_Specific;
	H_Specific = 0;
}

void Widget::WidgetImplementation::Show()
{
	if(H_Specific)
	{
#if defined(WS_X11)
#elif defined(WS_MSWINDOWS)
		if(H_Specific->H_Handle)
		{
//			WidgetSpecific::HandleMap.Append(H_Specific->H_Handle, H_Specific);
			ShowWindow(H_Specific->H_Handle, SW_SHOW);
		}
#endif
	}
}

void Widget::WidgetImplementation::Hide()
{
	if(H_Specific)
	{
#if defined(WS_X11)
#elif defined(WS_MSWINDOWS)
//		if(!(H_Specific->H_Handle))
//			H_Specific->H_Handle=CreateWindow("WS_WIDGET", NULL, NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
		ShowWindow(H_Specific->H_Handle, SW_HIDE);
#endif
	}
}

Layout* Widget::WidgetImplementation::GetLayout() const {
	if(H_Specific)
		return H_Specific->H_Layout;
	return 0;
}

void Widget::WidgetImplementation::SetLayout(Layout* LAYOUT) {
	if(H_Specific)
		H_Specific->H_Layout = LAYOUT;
}

integer Widget::WidgetImplementation::X() const {
	if(H_Specific)
		return H_Specific->I_X;
	return 0;
}

integer Widget::WidgetImplementation::Y() const {
	if(H_Specific)
		return H_Specific->I_Y;
	return 0;
}

uinteger Widget::WidgetImplementation::Width() const {
	if(H_Specific)
		return H_Specific->I_Width;
	return 0;
}

uinteger Widget::WidgetImplementation::Height() const {
	if(H_Specific)
		return H_Specific->I_Height;
	return 0;
}

void Widget::WidgetImplementation::Move(integer X, integer Y) {
	if(H_Specific)
	{
#if defined(WS_MSWINDOWS)
		if(H_Specific->H_Handle)
			MoveWindow(H_Specific->H_Handle, X, Y, 0, 0, true);
#endif
	}
}

void Widget::WidgetImplementation::Resize(uinteger WIDTH, uinteger HEIGHT) {
	if(H_Specific)
	{
#if defined(WS_MSWINDOWS)
		if(H_Specific->H_Handle)
			MoveWindow(H_Specific->H_Handle, 0, 0, WIDTH, HEIGHT, true);
#endif
	}
}

String Widget::WidgetImplementation::Title() const {
	if(H_Specific)
		return H_Specific->STR_Title;
	return String();
}

void Widget::WidgetImplementation::SetTitle(const String& REF) {
	if(H_Specific)
	{
#if defined(WS_MSWINDOWS)
		H_Specific->STR_Title = REF;
		if(H_Specific->H_Handle)
			SetWindowText(H_Specific->H_Handle, H_Specific->STR_Title.CString());
#endif
	}
}

Widget::Widget(Widget* PARENT):Object(static_cast<Object*>(PARENT)), Implementation(0)
{
	Implementation=new WidgetImplementation();
	if(Implementation)
		Implementation->Initial(this);
	else
		throw;
}

Widget::~Widget()
{
	if(Implementation)
	{
		Implementation->Cleanup();
		delete Implementation;
	}
}

Widget* Widget::Parent() const{return static_cast<Widget*>(GetMaster());}

boolean Widget::Visible() const{return false;}

void Widget::Show() {
	if(Implementation)
		Implementation->Show();
}

void Widget::Hide() {
	if(Implementation)
		Implementation->Hide();
}

String Widget::Title() const{
	if(Implementation)
		return Implementation->Title();
	return String();
}

void Widget::SetTitle(const String& REF) {
	if(Implementation)
		Implementation->SetTitle(REF);
}

Layout* Widget::GetLayout() const {
	if(Implementation)
		return Implementation->GetLayout();
	return 0;
}

void Widget::SetLayout(Layout* LAYOUT) {
	if(Implementation)
		Implementation->SetLayout(LAYOUT);
}

integer Widget::X() const {
	if(Implementation)
		return Implementation->X();
	return 0;
}

integer Widget::Y() const {
	if(Implementation)
		return Implementation->Y();
	return 0;
}

uinteger Widget::Width() const {
	if(Implementation)
		return Implementation->Width();
	return 0;
}

uinteger Widget::Height() const {
	if(Implementation)
		return Implementation->Height();
	return 0;
}

void Widget::Move(integer X, integer Y) {
	if(Implementation)
		Implementation->Move(X, Y);
}

void Widget::Resize(uinteger W, uinteger H) {
	if(Implementation)
		Implementation->Resize(W, H);
}

void Widget::Render() {}

void Widget::ProcessEvent(Event* E)
{
	if(E)
	{
		if(E->Type==WS_GUIEVENT)
			return ProcessGUIEvent(static_cast<GUIEvent*>(E));
		return Object::ProcessEvent(E);
	}
}

void Widget::ProcessGUIEvent(GUIEvent* E)
{
	if(E)
		switch(E->EventType)
		{
		case GUIEvent::RenderEvent:
			return ProcessRenderEvent(static_cast<RenderEvent*>(E));
			break;
		case GUIEvent::ResizeEvent:
			return ProcessResizeEvent(static_cast<ResizeEvent*>(E));
			break;
		case GUIEvent::MoveEvent:
			return ProcessMoveEvent(static_cast<MoveEvent*>(E));
			break;
		case GUIEvent::InputEvent:
			return ProcessInputEvent(static_cast<InputEvent*>(E));
			break;
		}
}

void Widget::ProcessRenderEvent(RenderEvent* E)
{
	if(E)
		return Render();
}

void Widget::ProcessResizeEvent(ResizeEvent* E) {
	Layout* H_Layout = GetLayout();
	if(H_Layout)
		H_Layout->Adjust();
}

void Widget::ProcessMoveEvent(MoveEvent* E) {}

void Widget::ProcessInputEvent(InputEvent* E)
{
	if(E)
		switch(E->Device)
		{
		case InputEvent::Cursor:
			return ProcessCursorEvent(static_cast<CursorEvent*>(E));
			break;
		case InputEvent::Key:
			return ProcessKeyEvent(static_cast<KeyEvent*>(E));
			break;
		}
}

void Widget::ProcessCursorEvent(CursorEvent* E) {}

void Widget::ProcessKeyEvent(KeyEvent* E) {}

END_WS_NAMESPACE

END_SOURCECODE
